React is a popular library for creating web apps and mobile apps.
In this article, we’ll look at how to improve the performance and avoiding antipatterns when we create React apps.
Reconciliation and keys
We’ve to add a key to the key
prop that’s unique.
Otherwise, React can’t keep track of the list items properly.
For instance, we can write:
{items.map(item => <li key={item.id}>{item.name}</li>)}
We got to have an id
property that’s unique with for each items
entry.
The items that are inserted can’t be tracked without a unique ID in the key
prop.
useEffect
We should watch the items that we need to watch to update a derived value.
To do that we use the useEffect
hook.
For instance, we write:
useEffect(() => {
doSomething(foo);
}, [foo]);
Now we call doSomething
only when foo
updates.
This is the hooks equivalent of shouldComponentUpdate
and componentWillReceiveProps
.
It works with both props and state changes.
Stateless Function Components
Stateless function components don’t give any benefits with regards to performance.
It just helps us make presentation only components to separate logic from components with states.
useMemo
We can use the useMemo
hook to store results from expensive computations.
For instance, we can write:
const memoizedValue = useMemo(() => compute(a, b), [a, b]);
Now React won’t compute the value again unless a
or b
changed.
Refactoring and Good Design
We should have states with descriptive names.
items
is a good name for a state that stores some items.
If we have something more specific then we should name it with that.
We should only render when something changes.
If props and state change, then we render.
Immutability
We should make things immutable when we can,
This way, they can’t be mutated accidentally.
Then we can avoid this kind of bugs, which may be hard to trace.
To avoid mutation, we can make a copy of an object and then change it.
We can use Object.assign
or spread to do that:
const newObj = Object.assign({}, obj, { foo: 'bar' });
or:
const newObj = {...obj, foo: 'bar' };
Likewise, we can do the same with arrays.
For instance, we can write:
const newArr = [...arr, 1];
We used the spread operator to spread the arr
array entries into a new array and add 1 to it.
Initializing the State using Props
We shouldn’t initialize the state using props.
Instead, we should watch for prop changes and then update the state accordingly.
This way, we’ll make sure our props are updated with the state.
To do that, we use the useEffect
hook as follows:
useEffect(() => {
setBar(foo);
}, [foo]);
If foo
is a prop, then we can set the bar
state with setBar
by calling it with foo
so that the bar
state is updated when foo
is updated.
Mutating the State
Also, we shouldn’t mutate the state directly.
If we have a state, then we shouldn’t assign it with a new value direcyly.
Instead, we call the function to set the state.
For instance, we can write:
setCount(2);
top set the state, where setCount
is the function returned by useState
to set the count
state.
Or we can write:
setCount(count => count + 1);
if we want to set a state based on the previous count
state’s value.
We should do that as little as possible to improve performance.
Using Indexes as a Key
Indexes shouldn’t be used as the key if possible.
This is because tracking can’t be done properly with indexes as keys, then if we modify the array of elements or components, then we would have problems.
The best value for the key prop is a unique ID of the item.
Spreading Props on DOM Elements
We should avoid spreading props to DOM elements to avoid spreading unknown HTML attributes, which is a bad practice.
We may get errors from React telling us to remove unknown attributes.
Instead, we should just write them out explicitly unlike props.
Conclusion
We should think about performance and antipatterns when we’re writing React components.
It’s hard to create components that are speedy and do everything we want.
But if we take into account some good practices, we can do it easily.